#region Apache License
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using IPSim.Data;
using IPSim.Util;

namespace IPSim.Core
{
    class TCPConnection
    {
        //private State m_state;
        private IPAddress m_remoteIPAddress;
        private IPInterface m_ipInterface;
        private byte m_remotePort;
        //private byte m_localByte;
        //private byte m_sequenceNumber;

        public TCPConnection(IPInterface ipInterface, IPAddress ipAddr, byte port)
        {
            m_ipInterface = ipInterface;
            m_remoteIPAddress = ipAddr;
            m_remotePort = port;
            //m_state = State.TIMEWAIT;
        }

        private enum State : byte
        { 
            LISTEN,
            SYNSENT,
            SYNRECEIVED,
            ESTABLISHED,
            FINWAIT1,
            FINWAIT2,
            CLOSEWAIT,
            CLOSING,
            LASTACK,
            TIMEWAIT,
            CLOSED
        }
    }


    public class TCPInterface
    {
        /// <summary>
        /// Buffer of UDPPackets recieved from IP Interface
        /// </summary>
        private Queue<IPSim.Data.TCPPacket> m_tcpBuffer;
        /// <summary>
        /// IP Interface that current UDP Interface belong to.
        /// </summary>
        private IPInterface m_ipInterface;
        /// <summary>
        /// Thread collecting packets from buffer and calling process method for them.
        /// Consists of PacketProcessorMethod()
        /// </summary>
        private Thread m_packetProcessorThread;
        /// <summary>
        /// Method called by event when IP Interface have some packet to send.
        /// Checks if packet is UDP type, then put it in buffer.
        /// </summary>
        /// <param name="source"></param>
        /// <param name="e"></param>
        private void ReceiveIPData(object source, TransportIPDataEventArgs e)
        {
            TCPPacket tempPacket;
            if (e.IPData.DeclaringType == typeof(TCPPacket))
            {
                tempPacket = e.IPData as TCPPacket;
                lock (m_tcpBuffer)
                {
                   m_tcpBuffer.Enqueue(tempPacket);
                    Monitor.PulseAll(m_tcpBuffer);
                }
            }
        }
        /// <summary>
        /// Constructor, connects TCP Interface to given IP Interface
        /// </summary>
        /// <param name="ipInterface"></param>
        public TCPInterface(IPInterface ipInterface)
        {
            m_tcpBuffer = new Queue<TCPPacket>();
            m_ipInterface = ipInterface;
            m_ipInterface.TransportIPData += new TransportIPDataEventHandler(this.ReceiveIPData);

            m_packetProcessorThread = new Thread(new ThreadStart(PacketProcessorMethod));
            m_packetProcessorThread.Start();
            Logger.log("[" + m_ipInterface.IPAddress + "] TCP Interface CREATED!", LogLevel.INFO);
        }

        /// <summary>
        /// Thread method collecting packets from buffer and calling process method for them.
        /// </summary>
        private void PacketProcessorMethod()
        {
            TCPPacket tempPacket;
            while (true)
            {
                lock (m_tcpBuffer)
                {
                    tempPacket = null;
                    while (m_tcpBuffer.Count == 0) Monitor.Wait(m_tcpBuffer);
                    tempPacket = m_tcpBuffer.Dequeue();
                }
                ProcessPacket(tempPacket);
            }
        }


        public void SendTCPPacket(TCPPacket tcpPacket, IPAddress destIP)
        {
            Logger.log("[" + m_ipInterface.IPAddress + "] Sending TCP packet", LogLevel.INFO);
            m_ipInterface.SendEthernetData(tcpPacket, destIP, IPPacket.IPProtocol.TCP);
        }

        /// <summary>
        /// Method that process TCP packet received from IP interface
        /// </summary>
        /// <param name="udpPacket"></param>
        private void ProcessPacket(TCPPacket udpPacket)
        {
            Logger.log("[" + m_ipInterface.IPAddress + "] TCP packet received", LogLevel.INFO);
        }

    }
}
